package com.hup.utils.commons.start;

import lombok.extern.log4j.Log4j2;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;

import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author hugan
 * @apiNote 在启动类中加入注解 @ComponentScan(basePackages = {"com.hup.utils.commons.start", ""})
 * @date 2019/9/14
 */
@SuppressWarnings("SpringFacetCodeInspection")
@Log4j2
@Configuration
public class SpringContext implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    public SpringContext() {
        log.debug("");
    }

    /**
     * 测试时,手动初始化
     *
     * @param clz 需要注入的类
     */
    public static void initTestContext(Class<?>... clz) {
        SpringContext springContext = new SpringContext();
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        springContext.setApplicationContext(context);
        context.register(clz);
        context.refresh();
    }

    /**
     * 解决GUI预览时,依赖SpringContext的组件预览错误问题
     */
    private static <T> T onNeverInit(Class<T> clz) {
        log.error("", new Throwable("未初始化SpringContext"));
        return null;
    }

    @SuppressWarnings("NullableProblems")
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContext.applicationContext = applicationContext;
    }

    public static <T> T getBean(Class<T> clz) {
        if (applicationContext == null) return onNeverInit(clz);
        return applicationContext.getBean(clz);
    }

    public static <T> T getBean(String name, Class<T> clz) {
        return applicationContext.getBean(name, clz);
    }

    public static <T> T getBeanNullable(Class<T> clz) {
        try {
            /*
            不建议使用applicationContext.containsBean();来判断是否存在bean;
            原因:
            .代码没混淆时,一般没问题
            .代码混淆后,就要使用LongBeanNameGenerator;这会改变beanName的格式
            所以这里直接捕获异常
             */
            return applicationContext.getBean(clz);
        } catch (NoSuchBeanDefinitionException e) {
            return null;
        }
    }


    /**
     * @return hadExe
     */
    public static <T> boolean executeIfExist(Class<T> clz, Consumer<T> consumer) {
        T bean = getBeanNullable(clz);
        if (bean != null) {
            consumer.accept(bean);
            return true;
        } else {
            return false;
        }
    }

    public static <T, I, O> O executeIfExist(Class<T> clz, I param, Function<I, O> function) {
        T bean = getBeanNullable(clz);
        if (bean == null) return null;
        return function.apply(param);
    }

    /**
     * @return hadExe
     */
    public static <T> boolean executeForeach(Class<? extends List<T>> clz, Consumer<T> consumer) {
        List<T> list = getBeanNullable(clz);
        boolean hadExe = false;
        if (list != null) {
            for (T item : list) {
                consumer.accept(item);
                hadExe = true;
            }
        }
        return hadExe;
    }

    public static DefaultListableBeanFactory getBeanFactory() {
        return (DefaultListableBeanFactory) ((GenericApplicationContext) SpringContext.applicationContext).getBeanFactory();
    }

}
